#include "stdafx.h"
#include "net/MultipartReader.hpp"
#include "net/MessageHeader.hpp"
#include "stdlib/exception.hpp"
#include "stdlib/bugcheck.hpp"
using namespace JHCPP::stdlib;
using namespace JHCPP::stdlib::jhException;
#include "charset/ascii.hpp"
using JHCPP::charset::CAscii;

NET_NAMESPACE_BEGIN

//
// CMultipartStreamBuf
//
CMultipartStreamBuf::CMultipartStreamBuf(std::istream& istr, const std::string& boundary)
	: BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in),
	m_istr(istr),
	m_boundary(boundary),
	m_lastPart(false)
{
	jh_assert (!boundary.empty() && boundary.length() < STREAM_BUFFER_SIZE - 6);
}

CMultipartStreamBuf::~CMultipartStreamBuf()
{
}

int CMultipartStreamBuf::readFromDevice(char* buffer, std::streamsize length)
{
	jh_assert_dbg (length >= (std::streamsize)(m_boundary.length() + 6));

	static const int eof = std::char_traits<char>::eof();
	std::streambuf& buf = *m_istr.rdbuf();

	int n  = 0;
	int ch = buf.sbumpc();
	if (ch == eof) return -1;
	*buffer++ = (char) ch; ++n;
	if (ch == '\n' || (ch == '\r' && buf.sgetc() == '\n'))
	{
		if (ch == '\r')
		{
			ch = buf.sbumpc(); // '\n'
			*buffer++ = (char) ch; ++n;
		}
		ch = buf.sgetc();
		if (ch == '\r' || ch == '\n') return n;
		*buffer++ = (char) buf.sbumpc(); ++n;
		if (ch == '-' && buf.sgetc() == '-')
		{
			ch = buf.sbumpc(); // '-'
			*buffer++ = (char) ch; ++n;
			std::string::const_iterator it  = m_boundary.begin();
			std::string::const_iterator end = m_boundary.end();
			ch = buf.sbumpc();
			*buffer++ = (char) ch; ++n;
			while (it != end && ch == *it)
			{
				++it;
				ch = buf.sbumpc();
				*buffer++ = (char) ch; ++n;
			}
			if (it == end)
			{
				if (ch == '\n' || (ch == '\r' && buf.sgetc() == '\n'))
				{
					if (ch == '\r')
					{
						ch = buf.sbumpc(); // '\n'
					}
					return 0;					
				}
				else if (ch == '-' && buf.sgetc() == '-')
				{
					ch = buf.sbumpc(); // '-'
					m_lastPart = true;
					return 0;
				}
			}
		}
	}
	ch = buf.sgetc();
	while (ch != eof && ch != '\r' && ch != '\n' && n < length)
	{
		*buffer++ = (char) buf.sbumpc(); ++n;
		ch = buf.sgetc();
	}
	return n;
}

bool CMultipartStreamBuf::lastPart() const
{
	return m_lastPart;
}


//
// CMultipartIOS
//
CMultipartIOS::CMultipartIOS(std::istream& istr, const std::string& boundary) : m_buf(istr, boundary)
{
}

CMultipartIOS::~CMultipartIOS()
{
	try
	{
		m_buf.sync();
	}
	catch (...)
	{
	}
}

CMultipartStreamBuf* CMultipartIOS::rdbuf()
{
	return &m_buf;
}

bool CMultipartIOS::lastPart() const
{
	return m_buf.lastPart();
}


//
// CMultipartInputStream
//
CMultipartInputStream::CMultipartInputStream(std::istream& istr, const std::string& boundary)
	: CMultipartIOS(istr, boundary),
	std::istream(&m_buf)
{
}

CMultipartInputStream::~CMultipartInputStream()
{
}


//
// CMultipartReader
//


CMultipartReader::CMultipartReader(std::istream& istr) : m_istr(istr), m_pMPI(0)
{
}

CMultipartReader::CMultipartReader(std::istream& istr, const std::string& boundary)
	: m_istr(istr), m_boundary(boundary), m_pMPI(0)
{
}

CMultipartReader::~CMultipartReader()
{
	delete m_pMPI;
}

void CMultipartReader::nextPart(CMessageHeader& messageHeader)
{
	if (!m_pMPI)
	{
		if (m_boundary.empty())
			guessBoundary();
		else
			findFirstBoundary();
	}
	else if (m_pMPI->lastPart())
	{
		throw MultipartException("No more parts available");
	}
	parseHeader(messageHeader);
	delete m_pMPI;
	m_pMPI = new CMultipartInputStream(m_istr, m_boundary);
}

bool CMultipartReader::hasNextPart()
{
	return (!m_pMPI || !m_pMPI->lastPart()) && m_istr.good();
}

std::istream& CMultipartReader::stream() const
{
	jh_check_ptr (m_pMPI);

	return *m_pMPI;
}

const std::string& CMultipartReader::boundary() const
{
	return m_boundary;
}

void CMultipartReader::findFirstBoundary()
{
	std::string expect("--");
	expect.append(m_boundary);
	std::string line;
	line.reserve(expect.length());
	bool ok = true;
	do
	{
		ok = readLine(line, expect.length());
	}
	while (ok && line != expect);

	if (!ok) throw MultipartException("No boundary line found");
}

void CMultipartReader::guessBoundary()
{
	static const int eof = std::char_traits<char>::eof();
	int ch = m_istr.get();
	while (CAscii::isSpace(ch))
		ch = m_istr.get();
	if (ch == '-' && m_istr.peek() == '-')
	{
		m_istr.get();
		ch = m_istr.peek();
		while (ch != eof && ch != '\r' && ch != '\n')
		{
			m_boundary += (char) m_istr.get();
			ch = m_istr.peek();
		}
		if (ch == '\r' || ch == '\n')
			ch = m_istr.get();
		if (m_istr.peek() == '\n')
			m_istr.get();
	}
	else throw MultipartException("No boundary line found");
}

void CMultipartReader::parseHeader(CMessageHeader& messageHeader)
{
	messageHeader.clear();
	messageHeader.read(m_istr);
	int ch = m_istr.get();
	if (ch == '\r' && m_istr.peek() == '\n') ch = m_istr.get();
}

bool CMultipartReader::readLine(std::string& line, std::string::size_type n)
{
	static const int eof = std::char_traits<char>::eof();

	line.clear();
	int ch = m_istr.peek();
	while (ch != eof && ch != '\r' && ch != '\n')
	{
		ch = (char) m_istr.get();
		if (line.length() < n) line += ch;
		ch = m_istr.peek();
	}
	if (ch != eof) m_istr.get();
	if (ch == '\r' && m_istr.peek() == '\n') m_istr.get();
	return ch != eof;
}

NET_NAMESPACE_END
